package socketTransmission;

import java.net.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.*;

/**
 * This program implements the server side to receive object from client.
 * 
 * @author Ganggao Zhu
 * @version 1.0 2011/10/19
 * 
 * 
 */

public class MultiClientServer implements Runnable {

	private ServerSocket server;
	private Socket connection;
	private int serverPort;
	private boolean isStopped = false;
	private ExecutorService threadPool = Executors.newFixedThreadPool(10);
	private List<Socket> connectionList = new ArrayList<Socket>();
	private Map<Socket,SocketConnection> serverConnectionMap = new HashMap<Socket,SocketConnection>();
	private Map<Socket,Server> serverMap = new HashMap<Socket,Server>();
	private int i_connectionNumber = 0;
	
	MultiClientServer(int serverPort) {
		this.serverPort = serverPort;
	}

	//main thread to accept the client request and 
	//run a server thread which will handle the protocol of the server.
	public void run() {

		openServerSocket();

		while (!isStopped()) {

			try {
				connection = server.accept();
				connectionList.add(i_connectionNumber,connection);
				i_connectionNumber++;
			} catch (IOException e) {
				if (isStopped()) {
					System.out.println("Server stopped");
					return;
				}
				throw new RuntimeException("Error accepting client connection",
						e);
			}
			SocketConnection socketConnection = new SocketConnection(connection);
			serverConnectionMap.put(connection, socketConnection);
			Server s = new Server(socketConnection);
			serverMap.put(connection, s);
			threadPool.execute(s);		
		}
		
		threadPool.shutdown();
		
		for (int i = 0; i < i_connectionNumber; i++) {
			
			Server sTemp = serverMap.get(connectionList.get(i));
			sTemp.stop();
			SocketConnection s = serverConnectionMap.get(connectionList.get(i));
			s.closeConnection();
		}
		
		System.out.println("Server stopped");	
	}
	
	
	//to know if the server stopped
	private synchronized boolean isStopped(){
		return this.isStopped;
	}
	
	//stop the server
	public synchronized void stop(){
		isStopped = true;
		try{
			server.close();
		}catch(IOException e){
			throw new RuntimeException("Error closing server",e);
		}
	}
	
	// create a server socket and wait for connection
	private void openServerSocket(){
		try{
			
			server = new ServerSocket(serverPort);
			System.out.println("Waiting for connection...");
			
		}catch(IOException e){
			throw new RuntimeException("Cannot open port "+serverPort,e);
		}
	}

}